package com.cctc.rds.scheduler.server.util;

/**
 * 任务标识生成器 [雪花算法]。
 */
public class TaskIdGenerator {
    private static final SnowFlake SNOW_FLAKE =
            new SnowFlake(1, Math.abs(IpAndPidUtils.getIp().hashCode()) % SnowFlake.MAX_MACHINE);

    public static long genTaskId() {
        return SNOW_FLAKE.nextId();
    }

    private static class SnowFlake {
        private static final long START_TIMESTAMP = 0L;
        private static final long DATACENTER_BIT = 5, MACHINE_BIT = 5, SEQUENCE_BIT = 12;
        private static final long MAX_DATACENTER = -1 ^ (-1 << DATACENTER_BIT);
        private static final long MAX_MACHINE = -1 ^ (-1 << MACHINE_BIT);
        private static final long MAX_SEQUENCE = -1 ^ (-1 << SEQUENCE_BIT);
        private static final long MACHINE_BIT_OFFSET = SEQUENCE_BIT;
        private static final long DATACENTER_BIT_OFFSET = MACHINE_BIT_OFFSET + MACHINE_BIT;
        private static final long TIMESTAMP_BIT_OFFSET = DATACENTER_BIT_OFFSET + DATACENTER_BIT;
        private long lastTimestamp = -1L, datacenterId, machineId, sequence = 0L;

        public SnowFlake(long datacenterId, long machineId) {
            this.datacenterId = datacenterId;
            this.machineId = machineId;
        }

        public synchronized long nextId() {
            long curTimestamp = System.currentTimeMillis();
            if (curTimestamp < lastTimestamp) {
                throw new RuntimeException("当前机器时钟出现回拨，请重新生成 ID.");
            } else if (curTimestamp == lastTimestamp) {
                sequence = (sequence + 1) & MAX_SEQUENCE;
                if (sequence == 0L) {
                    curTimestamp = getNextTimestamp();
                }
            } else {
                sequence = 0L;
            }
            lastTimestamp = curTimestamp;
            return (curTimestamp - START_TIMESTAMP) << TIMESTAMP_BIT_OFFSET
                    | datacenterId << DATACENTER_BIT_OFFSET
                    | machineId << MACHINE_BIT_OFFSET
                    | sequence;
        }

        private long getNextTimestamp() {
            long curTimestamp = System.currentTimeMillis();
            while (curTimestamp <= lastTimestamp) curTimestamp = lastTimestamp;
            return curTimestamp;
        }

    }

    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis());
//        for (int i = 0; i < 10; i++) {
//            System.out.println(genTaskId());
//        }
    }

}
